use regex::Regex;
use std::collections::HashMap;

fn is_leap_year(year: i32) -> bool {
    // 判断是否为闰年
    year % 4 == 0 && (year % 100!= 0 || year % 400 == 0)
}

fn check_date_valid(year: i32, month: u32, day: u32) -> bool {
    // 检查日期的有效性
    if month > 12 || month < 1 {
        return false;
    }
    let month_days = match month {
        1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
        4 | 6 | 9 | 11 => 30,
        2 => if is_leap_year(year) { 29 } else { 28 },
        _ => return false,
    };
    if day > month_days || day < 1 {
        return false;
    }
    true
}

fn weekday_of_date(mut year: i32,mut month: u32, day: u32) -> u32 {
    // Zeller 算法计算某一天是星期几, 0表示星期天
    if month < 3 {
        year -= 1;
        month += 12;
    }
    let c = year / 100;
    let y = year % 100;
    let w = y + y / 4 + c / 4 - 2 * c + 26 * (month as i32 + 1) / 10 + day as i32 - 1;
    ((w + 7) % 7) as u32
}

fn day_of_year(year: i32, month: u32, day: u32) -> u32 {
    // 计算某年某月某日是这一年的第几天
    let days_of_month = [
        31, if is_leap_year(year) { 29 } else { 28 }, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
    ];
    let mut sum = day;
    for i in 0..(month - 1) {
        sum += days_of_month[i as usize];
    }
    sum
}

fn week_of_year(year: i32, month: u32, day: u32) -> u32 {
    // 计算某年某月某日是这一年的第几周
    let weekday_of_january_4 = weekday_of_date(year, 1, 4);
    let day_of_first_week_first_day: i32 = 5 - if weekday_of_january_4 > 0 { weekday_of_january_4  as  i32 } else { 7 };
    let day_of_year = day_of_year(year, month, day);
    let ret = ((day_of_year as i32 - day_of_first_week_first_day + 7) / 7 ) as u32;
    let weekday_of_next_january_4 = weekday_of_date( year + 1, 1, 4);
    let day_of_next_january_4 = if is_leap_year(year) { 366 } else { 365 } + 4;
    let to_next_january_4 = day_of_next_january_4 - day_of_year;
    let check_bound = if weekday_of_next_january_4 > 0 { weekday_of_next_january_4 } else { 7 };
    if to_next_january_4 < check_bound {
        1
    } else {
        ret
    }
}

fn remain_days(year: i32, month: u32, day: u32) -> u32 {
    // 计算这一年度还剩多少天
    let total_days = if is_leap_year(year) { 366 } else { 365 };
    let day_of_year = day_of_year(year, month, day);
    total_days - day_of_year 
}

fn remain_lunar_days(year: i32, month: u32, day: u32) -> u32 {
    // 计算这一年度还剩多少农历天，春节时间打表
    let spring_festival_dates: HashMap<i32, (u32, u32)> = HashMap::from([
        (2001, (1, 24)),
        (2002, (2, 12)),
        (2003, (2, 1)),
        (2004, (1, 22)),
        (2005, (2, 9)),
        (2006, (1, 29)),
        (2007, (2, 18)),
        (2008, (2, 7)),
        (2009, (1, 26)),
        (2010, (2, 14)),
        (2011, (2, 3)),
        (2012, (1, 23)),
        (2013, (2, 10)),
        (2014, (1, 31)),
        (2015, (2, 19)),
        (2016, (2, 8)),
        (2017, (1, 28)),
        (2018, (2, 16)),
        (2019, (2, 5)),
        (2020, (1, 25)),
        (2021, (2, 12)),
        (2022, (2, 1)),
        (2023, (1, 22)),
        (2024, (2, 10)),
        (2025, (1, 29)),
        (2026, (2, 17)),
        (2027, (2, 6)),
        (2028, (1, 26)),
        (2029, (2, 13)),
        (2030, (2, 3)),
        (2031, (1, 23)),
        (2032, (2, 11)),
        (2033, (1, 31)),
        (2034, (2, 19)),
        (2035, (2, 8)),
        (2036, (1, 28)),
        (2037, (2, 15)),
        (2038, (2, 4)),
        (2039, (1, 24)),
        (2040, (2, 12)),
        (2041, (2, 1)),
        (2042, (1, 22)),
        (2043, (2, 10)),
        (2044, (1, 30)),
        (2045, (2, 17)),
        (2046, (2, 6)),
        (2047, (1, 26)),
        (2048, (2, 14)),
        (2049, (2, 2)),
        (2050, (1, 23)),
    ]);
    let day_count = day_of_year(year, month, day);
    let spring_festival_date_of_this_year = if let Some(date) = spring_festival_dates.get(&year) {
        date
    } else {
        &(2, 1)
    };
    let day_of_spring_festival_of_this_year = day_of_year(year, spring_festival_date_of_this_year.0, spring_festival_date_of_this_year.1);
    if day_count < day_of_spring_festival_of_this_year {
        day_of_spring_festival_of_this_year - day_count - 1
    } else {
        let next_festival_date = if let Some(date) = spring_festival_dates.get(&(year + 1)) {
            date
        } else {
            &(2, 1)
        };
        let day_of_next_festival_date = day_of_year(year + 1, next_festival_date.0, next_festival_date.1) + if is_leap_year(year) { 366 } else { 365 };
        day_of_next_festival_date - day_count - 1
    }
}

pub fn time_info(time: &str) -> String {
    let re = Regex::new(r"^(\d+)-(\d{2})-(\d{2})$").unwrap();
    let mut ans: String = String::new();
    if let Some(cap) = re.captures(time) {
        if let (Ok(year), Ok(month), Ok(day)) = (cap[1].parse::<i32>(), cap[2].parse::<u32>(), cap[3].parse::<u32>()) {
            if check_date_valid(year, month, day) {
                ans = format!("{},{},{}", week_of_year(year, month, day), remain_days(year, month, day), remain_lunar_days(year, month, day));
            }
        } 
    }
    ans
}
